package nodes

import (
	"sync"

	"example.com/dcache/consistenthash"
	"example.com/dcache/setting"
)

/*
ClientPool is used for storing all nodes and according handlers,
it is used by current server to connect with other servers
*/
type BrotherClientsPool struct {
	//Self server's IP address, eg: 127.0.0.1:10240
	self string
	//Pre url after IP address, "/api", then fullURL should be like "127.0.0.1:10240/api"
	httpBasePath string
	//Lock
	mu sync.Mutex
	//All nodes, including self, 127.0.0.1:10240, 127.0.0.1:10241, 127.0.0.1:10242 ......
	//You can compare node which in nodes with self, to judge if the node is self or not
	nodesAddresses []string
	nodes          *consistenthash.Map
	//Store node and according handler
	NodeHandlers map[string]NodeHandler
}

// New is the constructor of ClientPool
func NewBrotherClientsPool(self string, httpBasePath string, nodes *consistenthash.Map) *BrotherClientsPool {
	return &BrotherClientsPool{
		self:         self,
		httpBasePath: httpBasePath,
		nodes:        nodes,
	}
}

func (c *BrotherClientsPool) AddNodes(nodesMap map[string]string) {
	c.mu.Lock()
	defer c.mu.Unlock()

	//get all http addr
	nodes := make([]string, 0, len(nodesMap))
	for node := range nodesMap {
		nodes = append(nodes, node)
	}

	c.nodesAddresses = nodes
	c.nodes.Add(nodes...)

	c.NodeHandlers = make(map[string]NodeHandler, len(nodes))
	//To-Do here, add rpc handler
	if setting.ServerSetting.UseRpc == 1 {
		for httpAddr, rpcAddr := range nodesMap {
			c.NodeHandlers[httpAddr] = NewRpcHandler(rpcAddr)
		}
	} else {
		for httpAddr := range nodesMap {
			c.NodeHandlers[httpAddr] = NewHttpHandler(httpAddr + c.httpBasePath)
		}
	}
}

func (c *BrotherClientsPool) PickNode(key string) (handler NodeHandler, ok bool) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if node := c.nodes.Get(key); node != "" && node != c.self {
		return c.NodeHandlers[node], true
	} else if node == c.self {
		return c.NodeHandlers[node], false
	}

	return nil, false
}

func (c *BrotherClientsPool) PickAllNodes() []NodeHandler {
	c.mu.Lock()
	defer c.mu.Unlock()

	handlers := []NodeHandler{}
	for node, handler := range c.NodeHandlers {
		if node != c.self {
			handlers = append(handlers, handler)
		}
	}

	return handlers
}

func (c *BrotherClientsPool) GetSelfAddress() string {
	return c.self
}

// GetAllNodes returns all nodes, including self
func (c *BrotherClientsPool) GetAllNodes() []string {
	return c.nodesAddresses
}

// GetAllNodes returns all nodes, except self
func (c *BrotherClientsPool) GetBrotherNodes() map[string]string {
	var addrs map[string]string = make(map[string]string)
	for _, addr := range c.nodesAddresses {
		if addr != c.self {
			addrs[addr] = c.NodeHandlers[addr].GetBaseAddr()
		}
	}
	return addrs
}
